# Persist State

import index from '!!raw-loader!@site/docs/examples/persist-state.ex';
import { LiveDemo } from '@site/components/LiveDemo';

The `persistState()` function gives you the ability to persist some of the app’s state, by saving it to `localStorage/sessionStorage` or anything that implements the `StorageEngine` API, and restore it after a refresh.

First, you need to install the package by using the CLI command `elf-cli install` and selecting the persist-state package, or via npm:

```bash
npm i @ngneat/elf-persist-state
```

To use it you should call the `persistState()` function, passing the store and the options:

```ts
import { createStore, withProps, select } from '@ngneat/elf';
import {
  persistState,
  localStorageStrategy,
  sessionStorageStrategy,
} from '@ngneat/elf-persist-state';

interface AuthProps {
  user: { id: string } | null;
}

const authStore = createStore({ name }, withProps<AuthProps>({ user: null }));

export const persist = persistState(authStore, {
  key: 'auth',
  storage: localStorageStrategy,
});

export const user$ = authStore.pipe(select((state) => state.user));
```

As the second parameter you should pass a `Options` object, which can be used to define the following:

- `storage`: an Object with `setItem`, `getItem` and `removeItem` method for storing the state (required).
- `source`: a method that receives the store and return what to save from it (by default - the entire store).
- `preStoreInit`: a method that runs upon initializing the store with a saved value, used for any required modifications before the value is set.
- `preStorageUpdate`: a method that runs before saving the store, used for any required modifications before the value is set.
- `key`: the name under which the store state is saved (by default - the store name plus a `@store` suffix).
- `runGuard` - returns whether the actual implementation should be run. The default is `() => typeof window !== 'undefined'`

<LiveDemo src={index} packages={['persist']} />

<br />
Elf also exposes the `initialized$` observable. This observable emits after Elf initialized
the stores based on the storage's value. For example:

```ts
import { persistState, localStorageStrategy } from '@ngneat/elf-persist-state';

const instance = persistState(todoStore, {
  key: 'todos',
  storage: localStorageStrategy,
});

instance.initialized$.subscribe(console.log);
```

## Async Support

This gives you the option to save a store’s value to a persistent storage, such as indexDB, websql, or any other asynchronous API. Here’s an example that leverages [localForage](https://github.com/localForage/localForage):

```ts
import * as localForage from 'localforage';

localForage.config({
  driver: localForage.INDEXEDDB,
  name: 'myApp',
  version: 1.0,
  storeName: 'auth',
});

export const persist = persistState(authStore, {
  key: 'auth',
  storage: localForage,
});
```

## Excluding keys from the state

The `excludeKeys()` operator can be used to exclude keys from the state:

```ts
import {
  excludeKeys,
  persistState,
  localStorageStrategy,
} from '@ngneat/elf-persist-state';

persistState(todoStore, {
  key: 'todos',
  storage: localStorageStrategy,
  source: () => todoStore.pipe(excludeKeys(['ids', 'entities'])),
});
```

## Performance Optimization

By default, the plugin will update the storage upon each store's change. Some applications perform multiple updates in a second, and update the storage on each change can be costly.

For such cases, it's recommended to use the `debounceTime` operator. For example:

```ts
import { persistState, localStorageStrategy } from '@ngneat/elf-persist-state';
import { debounceTime } from 'rxjs/operators';
persistState(todoStore, {
  key: 'todos',
  storage: localStorageStrategy,
  source: () => todoStore.pipe(debounceTime(1000)),
});
```

## preStorageUpdate

The `preStorageUpdate` option is a function that is called before the state is saved to the storage. It receives two parameters: `storeName` and `state`. The `storeName` is a string representing the name of the store, and `state` is the current state of the store.

This function is useful when you want to modify the state before it is saved to the storage. For example, you might want to remove some sensitive data or transform the state in some way.

The function should return the modified state which will be saved to the storage. If you don't return anything from this function, the original state will be saved.

Here is an example of how to use `preStorageUpdate`:

```ts
import { persistState, localStorageStrategy } from '@ngneat/elf-persist-state';

const preStorageUpdate = (storeName, state) => {
  const newState = { ...state };
  if (storeName === 'todos') {
    delete newState.sensitiveData;
  }
  return newState;
};

persistState(todoStore, {
  key: 'todos',
  storage: localStorageStrategy,
  preStorageUpdate,
});
```

In this example, the `preStorageUpdate` function removes the `sensitiveData` property from the state before it is saved to storage.
